Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* conn.c
3 : ** strophe XMPP client library -- connection object functions
4 : **
5 : ** Copyright (C) 2005-2009 Collecta, Inc.
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Connection management.
15 : */
16 :
17 : /** @defgroup Connections Connection management
18 : * These functions manage a connection object.
19 : *
20 : * A part of those functions is listed under the \ref TLS section.
21 : */
22 :
23 : #include <errno.h>
24 : #include <netinet/in.h>
25 : #include <stdarg.h>
26 : #include <string.h>
27 : #include <limits.h>
28 :
29 : #include "strophe.h"
30 :
31 : #include "common.h"
32 : #include "util.h"
33 : #include "parser.h"
34 :
35 : #ifndef DEFAULT_SEND_QUEUE_MAX
36 : /** @def DEFAULT_SEND_QUEUE_MAX
37 : * The default maximum send queue size. This is currently unused.
38 : */
39 : #define DEFAULT_SEND_QUEUE_MAX 64
40 : #endif
41 : #ifndef DISCONNECT_TIMEOUT
42 : /** @def DISCONNECT_TIMEOUT
43 : * The time to wait (in milliseconds) for graceful disconnection to
44 : * complete before the connection is reset. The default is 2 seconds.
45 : */
46 : #define DISCONNECT_TIMEOUT 2000 /* 2 seconds */
47 : #endif
48 : #ifndef CONNECT_TIMEOUT
49 : /** @def CONNECT_TIMEOUT
50 : * The time to wait (in milliseconds) for a connection attempt to succeed
51 : * or error. The default is 5 seconds.
52 : */
53 : #define CONNECT_TIMEOUT 5000 /* 5 seconds */
54 : #endif
55 :
56 : #ifndef KEEPALIVE_TIMEOUT
57 : /** @def KEEPALIVE_TIMEOUT
58 : * The time (in seconds) the connection needs to remain idle before TCP starts
59 : * sending keepalive probes, if the socket option SO_KEEPALIVE has been set on
60 : * this socket.
61 : * c.f. `TCP_KEEPIDLE` in `man 7 tcp` for linux, FreeBSD and some others or
62 : * `TCP_KEEPALIVE` on MacOS.
63 : */
64 : #define KEEPALIVE_TIMEOUT 60
65 : #endif
66 : #ifndef KEEPALIVE_INTERVAL
67 : /** @def KEEPALIVE_INTERVAL
68 : * The time (in seconds) between individual keepalive probes.
69 : * c.f. `TCP_KEEPINTVL` in `man 7 tcp`
70 : */
71 : #define KEEPALIVE_INTERVAL 30
72 : #endif
73 : #ifndef KEEPALIVE_COUNT
74 : /** @def KEEPALIVE_COUNT
75 : * The maximum number of keepalive probes TCP should send before dropping the
76 : * connection.
77 : * c.f. `TCP_KEEPCNT` in `man 7 tcp`
78 : */
79 : #define KEEPALIVE_COUNT 3
80 : #endif
81 :
82 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner);
83 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata);
84 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn);
85 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
86 : char **attributes,
87 : size_t attributes_len);
88 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
89 : char **attributes,
90 : size_t attributes_len);
91 : static void _conn_attributes_new(xmpp_conn_t *conn,
92 : char **attrs,
93 : char ***attributes,
94 : size_t *attributes_len);
95 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
96 : char **attributes,
97 : size_t attributes_len);
98 : static void _handle_stream_start(char *name, char **attrs, void *userdata);
99 : static void _handle_stream_end(char *name, void *userdata);
100 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata);
101 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
102 : xmpp_stanza_t *stanza);
103 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
104 : xmpp_conn_type_t type);
105 : static void _conn_reset(xmpp_conn_t *conn);
106 : static int _conn_connect(xmpp_conn_t *conn,
107 : const char *domain,
108 : xmpp_conn_type_t type,
109 : xmpp_conn_handler callback,
110 : void *userdata);
111 : static void _send_valist(xmpp_conn_t *conn,
112 : const char *fmt,
113 : va_list ap,
114 : xmpp_send_queue_owner_t owner);
115 : static int _send_raw(xmpp_conn_t *conn,
116 : char *data,
117 : size_t len,
118 : xmpp_send_queue_owner_t owner,
119 : void *userdata);
120 :
121 0 : void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text)
122 : {
123 0 : xmpp_stanza_t *error = xmpp_error_new(conn->ctx, type, text);
124 :
125 0 : send_stanza(conn, error, XMPP_QUEUE_STROPHE);
126 0 : }
127 :
128 : /** Create a new Strophe connection object.
129 : *
130 : * @param ctx a Strophe context object
131 : *
132 : * @return a Strophe connection object or NULL on an error
133 : *
134 : * @ingroup Connections
135 : */
136 8 : xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx)
137 : {
138 8 : xmpp_conn_t *conn = NULL;
139 8 : xmpp_connlist_t *tail, *item;
140 :
141 8 : if (ctx == NULL)
142 : return NULL;
143 :
144 8 : conn = strophe_alloc(ctx, sizeof(xmpp_conn_t));
145 8 : if (conn != NULL) {
146 8 : memset(conn, 0, sizeof(xmpp_conn_t));
147 8 : conn->ctx = ctx;
148 :
149 8 : conn->type = XMPP_UNKNOWN;
150 8 : conn->state = XMPP_STATE_DISCONNECTED;
151 :
152 8 : conn->sock = INVALID_SOCKET;
153 8 : conn->ka_timeout = KEEPALIVE_TIMEOUT;
154 8 : conn->ka_interval = KEEPALIVE_INTERVAL;
155 8 : conn->ka_count = KEEPALIVE_COUNT;
156 :
157 : /* default send parameters */
158 8 : conn->send_queue_max = DEFAULT_SEND_QUEUE_MAX;
159 :
160 : /* default timeouts */
161 8 : conn->connect_timeout = CONNECT_TIMEOUT;
162 :
163 8 : conn->lang = strophe_strdup(conn->ctx, "en");
164 8 : if (!conn->lang) {
165 0 : strophe_free(conn->ctx, conn);
166 0 : return NULL;
167 : }
168 8 : tls_clear_password_cache(conn);
169 8 : conn->password_retries = 1;
170 :
171 16 : conn->parser =
172 8 : parser_new(conn->ctx, _handle_stream_start, _handle_stream_end,
173 : _handle_stream_stanza, conn);
174 : /* we own (and will free) the hash values */
175 8 : conn->id_handlers = hash_new(conn->ctx, 32, NULL);
176 :
177 : /* give the caller a reference to connection */
178 8 : conn->ref = 1;
179 :
180 : /* add connection to ctx->connlist */
181 8 : tail = conn->ctx->connlist;
182 8 : while (tail && tail->next)
183 : tail = tail->next;
184 :
185 8 : item = strophe_alloc(conn->ctx, sizeof(xmpp_connlist_t));
186 8 : if (!item) {
187 0 : strophe_error(conn->ctx, "xmpp", "failed to allocate memory");
188 0 : strophe_free(conn->ctx, conn->lang);
189 0 : parser_free(conn->parser);
190 0 : strophe_free(conn->ctx, conn);
191 0 : conn = NULL;
192 : } else {
193 8 : item->conn = conn;
194 8 : item->next = NULL;
195 :
196 8 : if (tail)
197 0 : tail->next = item;
198 : else
199 8 : conn->ctx->connlist = item;
200 : }
201 : }
202 :
203 : return conn;
204 : }
205 :
206 : /** Clone a Strophe connection object.
207 : *
208 : * @param conn a Strophe connection object
209 : *
210 : * @return the same conn object passed in with its reference count
211 : * incremented by 1
212 : *
213 : * @ingroup Connections
214 : */
215 0 : xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn)
216 : {
217 0 : conn->ref++;
218 0 : return conn;
219 : }
220 :
221 : /** Register sockopt callback
222 : * Set function to be called when a new socket is created to allow setting
223 : * socket options before connection is started.
224 : *
225 : * If the connection is already connected, this callback will be called
226 : * immediately.
227 : *
228 : * To set options that can only be applied to disconnected sockets, the
229 : * callback must be registered before connecting.
230 : *
231 : * @param conn The Strophe connection object this callback is being registered
232 : * for
233 : * @param callback a xmpp_sockopt_callback callback function that will receive
234 : * notifications of connection status
235 : *
236 : * @ingroup Connections
237 : */
238 :
239 0 : void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
240 : xmpp_sockopt_callback callback)
241 : {
242 0 : conn->sockopt_cb = callback;
243 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
244 0 : callback(conn, &conn->sock);
245 0 : }
246 :
247 : /** Release a Strophe connection object.
248 : * Decrement the reference count by one for a connection, freeing the
249 : * connection object if the count reaches 0.
250 : *
251 : * @param conn a Strophe connection object
252 : *
253 : * @return TRUE if the connection object was freed and FALSE otherwise
254 : *
255 : * @ingroup Connections
256 : */
257 8 : int xmpp_conn_release(xmpp_conn_t *conn)
258 : {
259 8 : xmpp_ctx_t *ctx;
260 8 : xmpp_connlist_t *item, *prev;
261 8 : xmpp_handlist_t *hlitem, *thli;
262 8 : hash_iterator_t *iter;
263 8 : const char *key;
264 8 : int released = 0;
265 :
266 8 : if (conn->ref > 1)
267 0 : conn->ref--;
268 : else {
269 8 : ctx = conn->ctx;
270 :
271 8 : if (conn->state == XMPP_STATE_CONNECTING ||
272 : conn->state == XMPP_STATE_CONNECTED) {
273 0 : conn_disconnect(conn);
274 : }
275 :
276 : /* remove connection from context's connlist */
277 8 : if (ctx->connlist->conn == conn) {
278 8 : item = ctx->connlist;
279 8 : ctx->connlist = item->next;
280 8 : strophe_free(ctx, item);
281 : } else {
282 : prev = NULL;
283 : item = ctx->connlist;
284 0 : while (item && item->conn != conn) {
285 0 : prev = item;
286 0 : item = item->next;
287 : }
288 :
289 0 : if (!item) {
290 0 : strophe_error(ctx, "xmpp",
291 : "Connection not in context's list\n");
292 : } else {
293 0 : prev->next = item->next;
294 0 : strophe_free(ctx, item);
295 : }
296 : }
297 :
298 8 : _conn_reset(conn);
299 :
300 : /* free handler stuff
301 : * note that userdata is the responsibility of the client
302 : * and the handler pointers don't need to be freed since they
303 : * are pointers to functions */
304 :
305 8 : hlitem = conn->timed_handlers;
306 8 : while (hlitem) {
307 0 : thli = hlitem;
308 0 : hlitem = hlitem->next;
309 :
310 0 : strophe_free(ctx, thli);
311 : }
312 :
313 : /* id handlers
314 : * we have to traverse the hash table freeing list elements
315 : * then release the hash table */
316 8 : iter = hash_iter_new(conn->id_handlers);
317 8 : while ((key = hash_iter_next(iter))) {
318 0 : hlitem = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
319 0 : while (hlitem) {
320 0 : thli = hlitem;
321 0 : hlitem = hlitem->next;
322 0 : strophe_free(conn->ctx, thli->u.id);
323 0 : strophe_free(conn->ctx, thli);
324 : }
325 : }
326 8 : hash_iter_release(iter);
327 8 : hash_release(conn->id_handlers);
328 :
329 8 : hlitem = conn->handlers;
330 8 : while (hlitem) {
331 0 : thli = hlitem;
332 0 : hlitem = hlitem->next;
333 :
334 0 : if (thli->u.ns)
335 0 : strophe_free(ctx, thli->u.ns);
336 0 : if (thli->u.name)
337 0 : strophe_free(ctx, thli->u.name);
338 0 : if (thli->u.type)
339 0 : strophe_free(ctx, thli->u.type);
340 0 : strophe_free(ctx, thli);
341 : }
342 :
343 8 : parser_free(conn->parser);
344 :
345 8 : if (conn->jid)
346 0 : strophe_free(ctx, conn->jid);
347 8 : if (conn->pass)
348 0 : strophe_free(ctx, conn->pass);
349 8 : if (conn->lang)
350 8 : strophe_free(ctx, conn->lang);
351 8 : if (conn->tls_client_cert)
352 8 : strophe_free(ctx, conn->tls_client_cert);
353 8 : if (conn->tls_client_key)
354 2 : strophe_free(ctx, conn->tls_client_key);
355 8 : if (conn->tls_cafile)
356 0 : strophe_free(ctx, conn->tls_cafile);
357 8 : if (conn->tls_capath)
358 0 : strophe_free(ctx, conn->tls_capath);
359 8 : if (conn->sm_state)
360 0 : xmpp_free_sm_state(conn->sm_state);
361 8 : tls_clear_password_cache(conn);
362 8 : sock_free(conn->xsock);
363 8 : strophe_free(ctx, conn);
364 8 : released = 1;
365 : }
366 :
367 8 : return released;
368 : }
369 :
370 : /** Get the JID which is or will be bound to the connection.
371 : *
372 : * @param conn a Strophe connection object
373 : *
374 : * @return a string containing the full JID or NULL if it has not been set
375 : *
376 : * @ingroup Connections
377 : */
378 0 : const char *xmpp_conn_get_jid(const xmpp_conn_t *conn)
379 : {
380 0 : return conn->jid;
381 : }
382 :
383 : /**
384 : * Get the JID discovered during binding time.
385 : *
386 : * This JID will contain the resource used by the current connection.
387 : * This is useful in the case where a resource was not specified for
388 : * binding.
389 : *
390 : * @param conn a Strophe connection object.
391 : *
392 : * @return a string containing the full JID or NULL if it's not been discovered
393 : *
394 : * @ingroup Connections
395 : */
396 0 : const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn)
397 : {
398 0 : return conn->bound_jid;
399 : }
400 :
401 : /** Set the JID of the user that will be bound to the connection.
402 : * If any JID was previously set, it will be discarded. This should not be
403 : * be used after a connection is created. The function will make a copy of
404 : * the JID string. If the supplied JID is missing the node, SASL
405 : * ANONYMOUS authentication will be used.
406 : *
407 : * @param conn a Strophe connection object
408 : * @param jid a full or bare JID
409 : *
410 : * @ingroup Connections
411 : */
412 0 : void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
413 : {
414 0 : if (conn->jid)
415 0 : strophe_free(conn->ctx, conn->jid);
416 0 : conn->jid = strophe_strdup(conn->ctx, jid);
417 0 : }
418 :
419 : /** Set the Handler function which will be called when the TLS stack can't
420 : * verify the CA of the server we're trying to connect to.
421 : *
422 : * @param conn a Strophe connection object
423 : * @param hndl certfail Handler function
424 : *
425 : * @ingroup TLS
426 : */
427 0 : void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
428 : xmpp_certfail_handler hndl)
429 : {
430 0 : conn->certfail_handler = hndl;
431 0 : }
432 :
433 : /** Set the CAfile
434 : *
435 : * @param conn a Strophe connection object
436 : * @param path path to a certificate file
437 : *
438 : * @ingroup TLS
439 : */
440 0 : void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
441 : {
442 0 : if (conn->tls_cafile)
443 0 : strophe_free(conn->ctx, conn->tls_cafile);
444 0 : conn->tls_cafile = strophe_strdup(conn->ctx, path);
445 0 : }
446 :
447 : /** Set the CApath
448 : *
449 : * @param conn a Strophe connection object
450 : * @param path path to a folder containing certificates
451 : *
452 : * @ingroup TLS
453 : */
454 0 : void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
455 : {
456 0 : if (conn->tls_capath)
457 0 : strophe_free(conn->ctx, conn->tls_capath);
458 0 : conn->tls_capath = strophe_strdup(conn->ctx, path);
459 0 : }
460 :
461 : /** Retrieve the peer certificate
462 : *
463 : * The returned Certificate object must be free'd by calling
464 : * \ref xmpp_tlscert_free
465 : *
466 : * @param conn a Strophe connection object
467 : *
468 : * @return a Strophe Certificate object
469 : *
470 : * @ingroup TLS
471 : */
472 0 : xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn)
473 : {
474 0 : return tls_peer_cert(conn);
475 : }
476 :
477 : /** Set the Callback function which will be called when the TLS stack can't
478 : * decrypt a password protected key file.
479 : *
480 : * @param conn a Strophe connection object
481 : * @param cb The callback function that shall be called
482 : * @param userdata An opaque data pointer that will be passed to the callback
483 : *
484 : * @ingroup TLS
485 : */
486 3 : void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
487 : xmpp_password_callback cb,
488 : void *userdata)
489 : {
490 3 : conn->password_callback = cb;
491 3 : conn->password_callback_userdata = userdata;
492 3 : }
493 :
494 : /** Set the number of retry attempts to decrypt a private key file.
495 : *
496 : * In case the user enters the password manually it can be useful to
497 : * directly retry if the decryption of the key file failed.
498 : *
499 : * @param conn a Strophe connection object
500 : * @param retries The number of retries that should be tried
501 : *
502 : * @ingroup TLS
503 : */
504 0 : void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
505 : {
506 0 : if (retries == 0)
507 0 : conn->password_retries = 1;
508 : else
509 0 : conn->password_retries = retries;
510 0 : }
511 :
512 : /** Retrieve the path of the key file that shall be unlocked.
513 : *
514 : * This makes usually sense to be called from the
515 : * \ref xmpp_password_callback .
516 : *
517 : * @param conn a Strophe connection object
518 : *
519 : * @return a String of the path to the key file
520 : *
521 : * @ingroup TLS
522 : */
523 0 : const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn)
524 : {
525 0 : return conn->tls_client_key;
526 : }
527 :
528 : /** Set the Client Certificate and Private Key or PKCS#12 encoded file that
529 : * will be bound to the connection. If any of them was previously set, it
530 : * will be discarded. This should not be used after a connection is created.
531 : * The function will make a copy of the strings passed in.
532 : *
533 : * In case the Private Key is encrypted, a callback must be set via
534 : * \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
535 : * password.
536 : *
537 : * In case one wants to use a PKCS#12 encoded file, it should be passed via
538 : * the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
539 : * `key` is deprecated.
540 : *
541 : * @param conn a Strophe connection object
542 : * @param cert path to a certificate file or a P12 file
543 : * @param key path to a private key file or a P12 file
544 : *
545 : * @ingroup TLS
546 : */
547 8 : void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
548 : const char *const cert,
549 : const char *const key)
550 : {
551 8 : strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
552 8 : if (conn->tls_client_cert)
553 0 : strophe_free(conn->ctx, conn->tls_client_cert);
554 8 : conn->tls_client_cert = NULL;
555 8 : if (conn->tls_client_key)
556 0 : strophe_free(conn->ctx, conn->tls_client_key);
557 8 : conn->tls_client_key = NULL;
558 8 : if (cert && key) {
559 2 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
560 2 : conn->tls_client_key = strophe_strdup(conn->ctx, key);
561 6 : } else if (cert && !key) {
562 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, cert);
563 3 : } else if (!cert && key) {
564 3 : strophe_warn(conn->ctx, "xmpp",
565 : "xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
566 : "parameter is deprecated. Use 'cert' instead");
567 3 : conn->tls_client_cert = strophe_strdup(conn->ctx, key);
568 : }
569 8 : }
570 :
571 : /** Get the number of xmppAddr entries in the client certificate.
572 : *
573 : * @param conn a Strophe connection object
574 : *
575 : * @return the number of xmppAddr entries in the client certificate
576 : *
577 : * @ingroup TLS
578 : */
579 8 : unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *const conn)
580 : {
581 8 : return tls_id_on_xmppaddr_num(conn);
582 : }
583 :
584 : /** Get a specific xmppAddr entry.
585 : *
586 : * @param conn a Strophe connection object
587 : * @param n the index of the entry, starting at 0
588 : *
589 : * @return a string containing the xmppAddr or NULL if n is out of range
590 : *
591 : * @ingroup TLS
592 : */
593 24 : char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *const conn, unsigned int n)
594 : {
595 24 : return tls_id_on_xmppaddr(conn, n);
596 : }
597 :
598 : /** Get the password used for authentication of a connection.
599 : *
600 : * @param conn a Strophe connection object
601 : *
602 : * @return a string containing the password or NULL if it has not been set
603 : *
604 : * @ingroup Connections
605 : */
606 0 : const char *xmpp_conn_get_pass(const xmpp_conn_t *conn)
607 : {
608 0 : return conn->pass;
609 : }
610 :
611 : /** Set the password used to authenticate the connection.
612 : * If any password was previously set, it will be discarded. The function
613 : * will make a copy of the password string.
614 : *
615 : * @param conn a Strophe connection object
616 : * @param pass the password
617 : *
618 : * @ingroup Connections
619 : */
620 0 : void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
621 : {
622 0 : if (conn->pass)
623 0 : strophe_free(conn->ctx, conn->pass);
624 0 : conn->pass = pass ? strophe_strdup(conn->ctx, pass) : NULL;
625 0 : }
626 :
627 : /** Get the strophe context that the connection is associated with.
628 : * @param conn a Strophe connection object
629 : *
630 : * @return a Strophe context
631 : *
632 : * @ingroup Connections
633 : */
634 0 : xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn)
635 : {
636 0 : return conn->ctx;
637 : }
638 :
639 : /** Initiate a connection to the XMPP server.
640 : * This function returns immediately after starting the connection
641 : * process to the XMPP server, and notifications of connection state changes
642 : * will be sent to the callback function. The domain and port to connect to
643 : * are usually determined by an SRV lookup for the xmpp-client service at
644 : * the domain specified in the JID. If SRV lookup fails, altdomain and
645 : * altport will be used instead if specified.
646 : *
647 : * @param conn a Strophe connection object
648 : * @param altdomain a string with domain to use if SRV lookup fails. If this
649 : * is NULL, the domain from the JID will be used.
650 : * @param altport an integer port number to use if SRV lookup fails. If this
651 : * is 0, the default port will be assumed.
652 : * @param callback a xmpp_conn_handler callback function that will receive
653 : * notifications of connection status
654 : * @param userdata an opaque data pointer that will be passed to the callback
655 : *
656 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
657 : *
658 : * @ingroup Connections
659 : */
660 0 : int xmpp_connect_client(xmpp_conn_t *conn,
661 : const char *altdomain,
662 : unsigned short altport,
663 : xmpp_conn_handler callback,
664 : void *userdata)
665 : {
666 0 : char *domain;
667 0 : int rc;
668 :
669 0 : if (!conn->jid && (conn->tls_client_cert || conn->tls_client_key)) {
670 0 : if (tls_id_on_xmppaddr_num(conn) != 1) {
671 0 : strophe_debug(conn->ctx, "xmpp",
672 : "Client certificate contains multiple or no xmppAddr "
673 : "and no JID was given to be used.");
674 0 : return XMPP_EINVOP;
675 : }
676 0 : conn->jid = tls_id_on_xmppaddr(conn, 0);
677 0 : if (!conn->jid)
678 : return XMPP_EMEM;
679 0 : strophe_debug(conn->ctx, "xmpp", "Use jid %s from id-on-xmppAddr.",
680 : conn->jid);
681 : }
682 :
683 0 : if (!conn->jid) {
684 0 : strophe_error(conn->ctx, "xmpp", "JID is not set.");
685 0 : return XMPP_EINVOP;
686 : }
687 :
688 0 : domain = xmpp_jid_domain(conn->ctx, conn->jid);
689 0 : if (!domain)
690 : return XMPP_EMEM;
691 :
692 0 : if (!conn->sm_state) {
693 0 : conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
694 0 : if (!conn->sm_state)
695 0 : goto err_mem;
696 0 : memset(conn->sm_state, 0, sizeof(*conn->sm_state));
697 0 : conn->sm_state->ctx = conn->ctx;
698 : }
699 :
700 0 : if (altdomain != NULL)
701 0 : strophe_debug(conn->ctx, "conn", "Connecting via altdomain.");
702 :
703 0 : if (conn->tls_legacy_ssl && !altdomain) {
704 : /* SSL tunneled connection on 5223 port is legacy and doesn't
705 : * have an SRV record. */
706 0 : altdomain = domain;
707 : }
708 0 : altport = altport ? altport : _conn_default_port(conn, XMPP_CLIENT);
709 :
710 0 : if (conn->xsock)
711 0 : sock_free(conn->xsock);
712 0 : conn->xsock = sock_new(conn, domain, altdomain, altport);
713 0 : if (!conn->xsock)
714 0 : goto err_mem;
715 :
716 0 : rc = _conn_connect(conn, domain, XMPP_CLIENT, callback, userdata);
717 0 : strophe_free(conn->ctx, domain);
718 :
719 0 : return rc;
720 :
721 0 : err_mem:
722 0 : strophe_free(conn->ctx, domain);
723 0 : return XMPP_EMEM;
724 : }
725 :
726 : /** Initiate a component connection to server.
727 : * This function returns immediately after starting the connection
728 : * process to the XMPP server, and notifications of connection state changes
729 : * will be sent to the internal callback function that will set up handler
730 : * for the component handshake as defined in XEP-0114.
731 : * The domain and port to connect to must be provided in this case as the JID
732 : * provided to the call serves as component identifier to the server and is
733 : * not subject to DNS resolution.
734 : *
735 : * @param conn a Strophe connection object
736 : * @param server a string with domain to use directly as the domain can't be
737 : * extracted from the component name/JID. If this is not set, the call
738 : * will fail.
739 : * @param port an integer port number to use to connect to server expecting
740 : * an external component. If this is 0, the port 5347 will be assumed.
741 : * @param callback a xmpp_conn_handler callback function that will receive
742 : * notifications of connection status
743 : * @param userdata an opaque data pointer that will be passed to the callback
744 : *
745 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
746 : *
747 : * @ingroup Connections
748 : */
749 0 : int xmpp_connect_component(xmpp_conn_t *conn,
750 : const char *server,
751 : unsigned short port,
752 : xmpp_conn_handler callback,
753 : void *userdata)
754 : {
755 : /* The server domain, jid and password MUST be specified. */
756 0 : if (!(server && conn->jid && conn->pass))
757 : return XMPP_EINVOP;
758 :
759 : /* XEP-0114 does not support TLS */
760 0 : (void)xmpp_conn_set_flags(conn, xmpp_conn_get_flags(conn) |
761 : XMPP_CONN_FLAG_DISABLE_TLS);
762 0 : if (!conn->tls_disabled) {
763 0 : strophe_error(conn->ctx, "conn",
764 : "Failed to disable TLS. "
765 : "XEP-0114 does not support TLS");
766 0 : return XMPP_EINT;
767 : }
768 :
769 0 : port = port ? port : _conn_default_port(conn, XMPP_COMPONENT);
770 0 : if (conn->xsock)
771 0 : sock_free(conn->xsock);
772 0 : conn->xsock = sock_new(conn, NULL, server, port);
773 0 : if (!conn->xsock)
774 : return XMPP_EMEM;
775 :
776 : /* JID serves as an identifier here and will be used as "to" attribute
777 : of the stream */
778 0 : return _conn_connect(conn, conn->jid, XMPP_COMPONENT, callback, userdata);
779 : }
780 :
781 : /** Initiate a raw connection to the XMPP server.
782 : * Arguments and behaviour of the function are similar to
783 : * xmpp_connect_client(), but it skips authentication process. In opposite to
784 : * xmpp_connect_client() during connection process two events are generated
785 : * instead of one. User's callback is called with event XMPP_CONN_RAW_CONNECT
786 : * when the TCP connection with the server is established. At this point user
787 : * might want to open an XMPP stream with xmpp_conn_open_stream() or establish
788 : * TLS session with xmpp_conn_tls_start(). Event XMPP_CONN_CONNECT is generated
789 : * when the XMPP stream is opened successfully and user may send stanzas over
790 : * the connection.
791 : *
792 : * This function doesn't use password nor node part of a jid. Therefore,
793 : * the only required configuration is a domain (or full jid) passed via
794 : * xmpp_conn_set_jid().
795 : *
796 : * @see xmpp_connect_client()
797 : *
798 : * @return XMPP_EOK (0) on success a number less than 0 on failure
799 : *
800 : * @ingroup Connections
801 : */
802 0 : int xmpp_connect_raw(xmpp_conn_t *conn,
803 : const char *altdomain,
804 : unsigned short altport,
805 : xmpp_conn_handler callback,
806 : void *userdata)
807 : {
808 0 : conn->is_raw = 1;
809 0 : return xmpp_connect_client(conn, altdomain, altport, callback, userdata);
810 : }
811 :
812 : /* Called when tcp connection is established. */
813 0 : void conn_established(xmpp_conn_t *conn)
814 : {
815 0 : if (conn->tls_legacy_ssl && !conn->is_raw) {
816 0 : strophe_debug(conn->ctx, "xmpp", "using legacy SSL connection");
817 0 : if (conn_tls_start(conn) != 0) {
818 0 : conn_disconnect(conn);
819 0 : return;
820 : }
821 : }
822 :
823 0 : if (conn->is_raw) {
824 0 : handler_reset_timed(conn, 0);
825 : /* we skip all the mandatory steps of the stream negotiation for a "raw"
826 : connection, but the event loop ignores user's handlers when
827 : conn->stream_negotiation_completed is not set. */
828 0 : conn->stream_negotiation_completed = 1;
829 0 : conn->conn_handler(conn, XMPP_CONN_RAW_CONNECT, 0, NULL,
830 : conn->userdata);
831 : } else {
832 : /* send stream init */
833 0 : conn_open_stream(conn);
834 : }
835 : }
836 :
837 : /** Send the default opening stream tag.
838 : * The default tag is the one sent by xmpp_connect_client().
839 : * User's connection handler is called with event XMPP_CONN_CONNECT when
840 : * server replies with its opening tag.
841 : *
842 : * @return XMPP_EOK (0) on success a number less than 0 on failure
843 : *
844 : * @note The connection must be connected with xmpp_connect_raw().
845 : *
846 : * @ingroup Connections
847 : */
848 0 : int xmpp_conn_open_stream_default(xmpp_conn_t *conn)
849 : {
850 0 : if (!conn->is_raw)
851 : return XMPP_EINVOP;
852 :
853 0 : conn_prepare_reset(conn, auth_handle_open_raw);
854 0 : conn_open_stream(conn);
855 :
856 0 : return XMPP_EOK;
857 : }
858 :
859 : /** Send an opening stream tag.
860 : * User's connection handler is called with event XMPP_CONN_CONNECT when
861 : * server replies with its opening tag.
862 : *
863 : * @param conn a Strophe connection object
864 : * @param attributes Array of strings in format: even index points to
865 : * an attribute name and odd index points to its value
866 : * @param attributes_len Number of elements in the attributes array, it
867 : * should be number of attributes multiplied by 2
868 : *
869 : * @return XMPP_EOK (0) on success a number less than 0 on failure
870 : *
871 : * @note The connection must be connected with xmpp_connect_raw().
872 : *
873 : * @ingroup Connections
874 : */
875 0 : int xmpp_conn_open_stream(xmpp_conn_t *conn,
876 : char **attributes,
877 : size_t attributes_len)
878 : {
879 0 : if (!conn->is_raw)
880 : return XMPP_EINVOP;
881 :
882 0 : conn_prepare_reset(conn, auth_handle_open_raw);
883 :
884 0 : return _conn_open_stream_with_attributes(conn, attributes, attributes_len);
885 : }
886 :
887 : /** Start synchronous TLS handshake with the server.
888 : *
889 : * @return XMPP_EOK (0) on success a number less than 0 on failure
890 : *
891 : * @ingroup Connections
892 : */
893 0 : int xmpp_conn_tls_start(xmpp_conn_t *conn)
894 : {
895 0 : return conn_tls_start(conn);
896 : }
897 :
898 : /** Cleanly disconnect the connection.
899 : * This function is only called by the stream parser when </stream:stream>
900 : * is received, and it not intended to be called by code outside of Strophe.
901 : *
902 : * @param conn a Strophe connection object
903 : */
904 0 : void conn_disconnect_clean(xmpp_conn_t *conn)
905 : {
906 : /* remove the timed handler */
907 0 : xmpp_timed_handler_delete(conn, _disconnect_cleanup);
908 :
909 0 : conn_disconnect(conn);
910 0 : }
911 :
912 : /** Disconnect from the XMPP server.
913 : * This function immediately disconnects from the XMPP server, and should
914 : * not be used outside of the Strophe library.
915 : *
916 : * @param conn a Strophe connection object
917 : */
918 0 : void conn_disconnect(xmpp_conn_t *conn)
919 : {
920 0 : strophe_debug(conn->ctx, "xmpp", "Closing socket.");
921 0 : conn->state = XMPP_STATE_DISCONNECTED;
922 0 : conn->stream_negotiation_completed = 0;
923 0 : if (conn->tls) {
924 0 : tls_stop(conn->tls);
925 0 : tls_free(conn->tls);
926 0 : conn->tls = NULL;
927 : }
928 0 : if (conn->sock != INVALID_SOCKET)
929 0 : sock_close(conn->sock);
930 0 : _reset_sm_state_for_reconnect(conn);
931 :
932 : /* fire off connection handler */
933 0 : conn->conn_handler(conn, XMPP_CONN_DISCONNECT, conn->error,
934 : conn->stream_error, conn->userdata);
935 0 : }
936 :
937 : /* prepares a parser reset. this is called from handlers. we can't
938 : * reset the parser immediately as it is not re-entrant. */
939 0 : void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler)
940 : {
941 0 : conn->reset_parser = 1;
942 0 : conn->open_handler = handler;
943 0 : }
944 :
945 : /* reset the parser */
946 0 : void conn_parser_reset(xmpp_conn_t *conn)
947 : {
948 0 : conn->reset_parser = 0;
949 0 : parser_reset(conn->parser);
950 0 : }
951 :
952 : /** Initiate termination of the connection to the XMPP server.
953 : * This function starts the disconnection sequence by sending
954 : * </stream:stream> to the XMPP server. This function will do nothing
955 : * if the connection state is different from CONNECTING or CONNECTED.
956 : *
957 : * @param conn a Strophe connection object
958 : *
959 : * @ingroup Connections
960 : */
961 0 : void xmpp_disconnect(xmpp_conn_t *conn)
962 : {
963 0 : if (conn->state != XMPP_STATE_CONNECTING &&
964 : conn->state != XMPP_STATE_CONNECTED)
965 : return;
966 :
967 : /* close the stream */
968 0 : send_raw_string(conn, "</stream:stream>");
969 :
970 : /* setup timed handler in case disconnect takes too long */
971 0 : handler_add_timed(conn, _disconnect_cleanup, DISCONNECT_TIMEOUT, NULL);
972 : }
973 :
974 : /** Send a raw string to the XMPP server.
975 : * This function is a convenience function to send raw string data to the
976 : * XMPP server. It is used by Strophe to send short messages instead of
977 : * building up an XML stanza with DOM methods. This should be used with care
978 : * as it does not validate the data; invalid data may result in immediate
979 : * stream termination by the XMPP server.
980 : *
981 : * @param conn a Strophe connection object
982 : * @param fmt a printf-style format string followed by a variable list of
983 : * arguments to format
984 : *
985 : * @ingroup Connections
986 : */
987 0 : void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
988 : {
989 0 : va_list ap;
990 :
991 0 : if (!_is_connected(conn, XMPP_QUEUE_USER))
992 0 : return;
993 :
994 0 : va_start(ap, fmt);
995 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_USER);
996 0 : va_end(ap);
997 : }
998 :
999 : /** Send raw bytes to the XMPP server.
1000 : * This function is a convenience function to send raw bytes to the
1001 : * XMPP server. It is used primarily by xmpp_send_raw_string(). This
1002 : * function should be used with care as it does not validate the bytes and
1003 : * invalid data may result in stream termination by the XMPP server.
1004 : *
1005 : * @param conn a Strophe connection object
1006 : * @param data a buffer of raw bytes
1007 : * @param len the length of the data in the buffer
1008 : *
1009 : * @ingroup Connections
1010 : */
1011 0 : void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len)
1012 : {
1013 0 : send_raw(conn, data, len, XMPP_QUEUE_USER, NULL);
1014 0 : }
1015 :
1016 : /** Send an XML stanza to the XMPP server.
1017 : * This is the main way to send data to the XMPP server. The function will
1018 : * terminate without action if the connection state is not CONNECTED.
1019 : *
1020 : * @param conn a Strophe connection object
1021 : * @param stanza a Strophe stanza object
1022 : *
1023 : * @ingroup Connections
1024 : */
1025 0 : void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
1026 : {
1027 0 : send_stanza(conn, xmpp_stanza_clone(stanza), XMPP_QUEUE_USER);
1028 0 : }
1029 :
1030 : /** Send the opening <stream:stream> tag to the server.
1031 : * This function is used by Strophe to begin an XMPP stream. It should
1032 : * not be used outside of the library.
1033 : *
1034 : * @param conn a Strophe connection object
1035 : */
1036 0 : void conn_open_stream(xmpp_conn_t *conn)
1037 : {
1038 0 : size_t attributes_len;
1039 0 : int rc;
1040 0 : char *from = NULL;
1041 0 : char *ns = conn->type == XMPP_CLIENT ? XMPP_NS_CLIENT : XMPP_NS_COMPONENT;
1042 0 : char *attributes[12] = {
1043 0 : "to", conn->domain, "xml:lang", conn->lang,
1044 : "version", "1.0", "xmlns", ns,
1045 : "xmlns:stream", XMPP_NS_STREAMS, "from", NULL};
1046 :
1047 0 : attributes_len = ARRAY_SIZE(attributes);
1048 0 : if (conn->tls && conn->jid && strchr(conn->jid, '@') != NULL)
1049 0 : from = xmpp_jid_bare(conn->ctx, conn->jid);
1050 :
1051 0 : if (from)
1052 0 : attributes[attributes_len - 1] = from;
1053 : else
1054 : attributes_len -= 2;
1055 :
1056 0 : rc = _conn_open_stream_with_attributes(conn, attributes, attributes_len);
1057 0 : if (rc != XMPP_EOK) {
1058 0 : strophe_error(conn->ctx, "conn",
1059 : "Cannot build stream tag: memory error");
1060 0 : conn_disconnect(conn);
1061 : }
1062 0 : if (from)
1063 0 : strophe_free(conn->ctx, from);
1064 0 : }
1065 :
1066 0 : int conn_interface_write(struct conn_interface *intf,
1067 : const void *buff,
1068 : size_t len)
1069 : {
1070 0 : int ret = intf->write(intf, buff, len);
1071 0 : if (ret < 0 && !intf->error_is_recoverable(intf, intf->get_error(intf))) {
1072 0 : intf->conn->error = intf->get_error(intf);
1073 : }
1074 0 : return ret;
1075 : }
1076 :
1077 0 : int conn_int_nop(struct conn_interface *intf)
1078 : {
1079 0 : UNUSED(intf);
1080 0 : return 0;
1081 : }
1082 :
1083 0 : int conn_tls_start(xmpp_conn_t *conn)
1084 : {
1085 0 : int rc;
1086 :
1087 0 : if (conn->tls_disabled) {
1088 0 : conn->tls = NULL;
1089 0 : rc = XMPP_EINVOP;
1090 : } else {
1091 0 : conn->tls = tls_new(conn);
1092 0 : rc = conn->tls == NULL ? XMPP_EMEM : 0;
1093 : }
1094 :
1095 0 : if (conn->tls != NULL) {
1096 0 : struct conn_interface old_intf = conn->intf;
1097 0 : conn->intf = tls_intf;
1098 0 : conn->intf.conn = conn;
1099 0 : if (tls_start(conn->tls)) {
1100 0 : conn->secured = 1;
1101 : } else {
1102 0 : rc = XMPP_EINT;
1103 0 : conn->error = tls_error(&conn->intf);
1104 0 : tls_free(conn->tls);
1105 0 : conn->tls = NULL;
1106 0 : conn->tls_failed = 1;
1107 0 : conn->intf = old_intf;
1108 : }
1109 : }
1110 0 : if (rc != 0) {
1111 0 : strophe_debug(conn->ctx, "conn",
1112 : "Couldn't start TLS! "
1113 : "error %d tls_error %d",
1114 : rc, conn->error);
1115 : }
1116 0 : return rc;
1117 : }
1118 :
1119 : /** Return applied flags for the connection.
1120 : *
1121 : * @param conn a Strophe connection object
1122 : *
1123 : * @return ORed connection flags that are applied for the connection.
1124 : *
1125 : * @ingroup Connections
1126 : */
1127 0 : long xmpp_conn_get_flags(const xmpp_conn_t *conn)
1128 : {
1129 0 : long flags;
1130 :
1131 0 : flags =
1132 0 : XMPP_CONN_FLAG_DISABLE_TLS * conn->tls_disabled |
1133 0 : XMPP_CONN_FLAG_MANDATORY_TLS * conn->tls_mandatory |
1134 0 : XMPP_CONN_FLAG_LEGACY_SSL * conn->tls_legacy_ssl |
1135 0 : XMPP_CONN_FLAG_TRUST_TLS * conn->tls_trust |
1136 0 : XMPP_CONN_FLAG_DISABLE_SM * conn->sm_disable |
1137 0 : XMPP_CONN_FLAG_ENABLE_COMPRESSION * conn->compression.allowed |
1138 0 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET * conn->compression.dont_reset |
1139 0 : XMPP_CONN_FLAG_LEGACY_AUTH * conn->auth_legacy_enabled;
1140 :
1141 0 : return flags;
1142 : }
1143 :
1144 : /** Set flags for the connection.
1145 : * This function applies set flags and resets unset ones. Default connection
1146 : * configuration is all flags unset. Flags can be applied only for a connection
1147 : * in disconnected state.
1148 : * All unsupported flags are ignored. If a flag is unset after successful set
1149 : * operation then the flag is not supported by current version.
1150 : *
1151 : * Supported flags are:
1152 : *
1153 : * - \ref XMPP_CONN_FLAG_DISABLE_TLS
1154 : * - \ref XMPP_CONN_FLAG_MANDATORY_TLS
1155 : * - \ref XMPP_CONN_FLAG_LEGACY_SSL
1156 : * - \ref XMPP_CONN_FLAG_TRUST_TLS
1157 : * - \ref XMPP_CONN_FLAG_LEGACY_AUTH
1158 : * - \ref XMPP_CONN_FLAG_DISABLE_SM
1159 : * - \ref XMPP_CONN_FLAG_ENABLE_COMPRESSION
1160 : * - \ref XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
1161 : *
1162 : * @param conn a Strophe connection object
1163 : * @param flags ORed connection flags
1164 : *
1165 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1166 : *
1167 : * @ingroup Connections
1168 : */
1169 0 : int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags)
1170 : {
1171 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1172 0 : strophe_error(conn->ctx, "conn",
1173 : "Flags can be set only "
1174 : "for disconnected connection");
1175 0 : return XMPP_EINVOP;
1176 : }
1177 0 : if ((flags & XMPP_CONN_FLAG_DISABLE_TLS) &&
1178 : (flags & (XMPP_CONN_FLAG_MANDATORY_TLS | XMPP_CONN_FLAG_LEGACY_SSL |
1179 : XMPP_CONN_FLAG_TRUST_TLS))) {
1180 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx conflict", flags);
1181 0 : return XMPP_EINVOP;
1182 : }
1183 :
1184 0 : conn->tls_disabled = (flags & XMPP_CONN_FLAG_DISABLE_TLS) ? 1 : 0;
1185 0 : conn->tls_mandatory = (flags & XMPP_CONN_FLAG_MANDATORY_TLS) ? 1 : 0;
1186 0 : conn->tls_legacy_ssl = (flags & XMPP_CONN_FLAG_LEGACY_SSL) ? 1 : 0;
1187 0 : conn->tls_trust = (flags & XMPP_CONN_FLAG_TRUST_TLS) ? 1 : 0;
1188 0 : conn->auth_legacy_enabled = (flags & XMPP_CONN_FLAG_LEGACY_AUTH) ? 1 : 0;
1189 0 : conn->sm_disable = (flags & XMPP_CONN_FLAG_DISABLE_SM) ? 1 : 0;
1190 0 : conn->compression.allowed =
1191 0 : (flags & XMPP_CONN_FLAG_ENABLE_COMPRESSION) ? 1 : 0;
1192 0 : conn->compression.dont_reset =
1193 0 : (flags & XMPP_CONN_FLAG_COMPRESSION_DONT_RESET) ? 1 : 0;
1194 0 : flags &= ~(XMPP_CONN_FLAG_DISABLE_TLS | XMPP_CONN_FLAG_MANDATORY_TLS |
1195 : XMPP_CONN_FLAG_LEGACY_SSL | XMPP_CONN_FLAG_TRUST_TLS |
1196 : XMPP_CONN_FLAG_LEGACY_AUTH | XMPP_CONN_FLAG_DISABLE_SM |
1197 : XMPP_CONN_FLAG_ENABLE_COMPRESSION |
1198 : XMPP_CONN_FLAG_COMPRESSION_DONT_RESET);
1199 0 : if (flags) {
1200 0 : strophe_error(conn->ctx, "conn", "Flags 0x%04lx unknown", flags);
1201 0 : return XMPP_EINVOP;
1202 : }
1203 :
1204 : return 0;
1205 : }
1206 :
1207 : /** Return whether TLS session is established or not.
1208 : *
1209 : * @return TRUE if TLS session is established and FALSE otherwise
1210 : *
1211 : * @ingroup Connections
1212 : */
1213 0 : int xmpp_conn_is_secured(xmpp_conn_t *conn)
1214 : {
1215 0 : return conn->secured && !conn->tls_failed && conn->tls != NULL;
1216 : }
1217 :
1218 : /**
1219 : * @return TRUE if connection is in connecting state and FALSE otherwise
1220 : *
1221 : * @ingroup Connections
1222 : */
1223 0 : int xmpp_conn_is_connecting(xmpp_conn_t *conn)
1224 : {
1225 0 : return conn->state == XMPP_STATE_CONNECTING ||
1226 0 : (conn->state == XMPP_STATE_CONNECTED &&
1227 0 : conn->stream_negotiation_completed == 0);
1228 : }
1229 :
1230 0 : static int _is_connected(xmpp_conn_t *conn, xmpp_send_queue_owner_t owner)
1231 : {
1232 0 : return conn->state == XMPP_STATE_CONNECTED &&
1233 0 : (owner != XMPP_QUEUE_USER ||
1234 0 : conn->stream_negotiation_completed == 1);
1235 : }
1236 :
1237 : /**
1238 : * @return TRUE if connection is established and FALSE otherwise
1239 : *
1240 : * @ingroup Connections
1241 : */
1242 0 : int xmpp_conn_is_connected(xmpp_conn_t *conn)
1243 : {
1244 0 : return _is_connected(conn, XMPP_QUEUE_USER);
1245 : }
1246 :
1247 : /**
1248 : * @return TRUE if connection is in disconnected state and FALSE otherwise
1249 : *
1250 : * @ingroup Connections
1251 : */
1252 0 : int xmpp_conn_is_disconnected(xmpp_conn_t *conn)
1253 : {
1254 0 : return conn->state == XMPP_STATE_DISCONNECTED;
1255 : }
1256 :
1257 : /**
1258 : * This returns the Stream Management state of a connection object after
1259 : * it has been disconnected.
1260 : * One can then initialise a fresh connection object and set this Stream
1261 : * Management state by calling \ref xmpp_conn_set_sm_state
1262 : *
1263 : * In case one wants to dispose of the state w/o setting it into a fresh
1264 : * connection object, one can call \ref xmpp_free_sm_state
1265 : *
1266 : * After calling this function to retrieve the state, only call one of the
1267 : * other two.
1268 : *
1269 : * @param conn a Strophe connection object
1270 : * @return The Stream Management state of the connection or NULL on error
1271 : *
1272 : * @ingroup Connections
1273 : */
1274 0 : xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn)
1275 : {
1276 0 : xmpp_sm_state_t *ret;
1277 :
1278 : /* We can only return the SM state when we're disconnected */
1279 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
1280 : return NULL;
1281 :
1282 0 : ret = conn->sm_state;
1283 0 : conn->sm_state = NULL;
1284 :
1285 0 : return ret;
1286 : }
1287 :
1288 0 : void xmpp_conn_set_sm_callback(xmpp_conn_t *conn,
1289 : xmpp_sm_callback cb,
1290 : void *ctx)
1291 : {
1292 0 : conn->sm_callback = cb;
1293 0 : conn->sm_callback_ctx = ctx;
1294 0 : }
1295 :
1296 : struct sm_restore {
1297 : xmpp_conn_t *conn;
1298 : const unsigned char *state;
1299 : const unsigned char *const state_end, *const orig;
1300 : };
1301 :
1302 0 : static int sm_load_u32(struct sm_restore *sm, uint8_t type, uint32_t *val)
1303 : {
1304 0 : if (*sm->state != type) {
1305 0 : strophe_error(
1306 0 : sm->conn->ctx, "conn",
1307 : "Invalid CBOR type at position %u: 0x%02x, expected: 0x%02x",
1308 0 : sm->state - sm->orig, *sm->state, type);
1309 0 : return XMPP_EINVOP;
1310 : }
1311 0 : sm->state++;
1312 0 : if ((sm->state + 4) > sm->state_end) {
1313 0 : strophe_error(sm->conn->ctx, "conn",
1314 : "Provided sm_state data is too short");
1315 0 : return XMPP_EINVOP;
1316 : }
1317 0 : uint32_t v;
1318 0 : memcpy(&v, sm->state, 4);
1319 0 : sm->state += 4;
1320 0 : *val = ntohl(v);
1321 0 : return 0;
1322 : }
1323 :
1324 0 : static int sm_load_string(struct sm_restore *sm, char **val, size_t *len)
1325 : {
1326 0 : uint32_t l;
1327 0 : int ret = sm_load_u32(sm, 0x7a, &l);
1328 0 : if (ret)
1329 0 : return ret;
1330 0 : if ((sm->state + l) > sm->state_end) {
1331 0 : strophe_error(sm->conn->ctx, "conn",
1332 : "Provided sm_state data is too short");
1333 0 : return XMPP_EINVOP;
1334 : }
1335 0 : *val = strophe_alloc(sm->conn->ctx, l + 1);
1336 0 : if (!*val)
1337 : return XMPP_EMEM;
1338 0 : memcpy(*val, sm->state, l);
1339 0 : (*val)[l] = '\0';
1340 0 : sm->state += l;
1341 0 : *len = l;
1342 0 : return 0;
1343 : }
1344 :
1345 0 : int xmpp_conn_restore_sm_state(xmpp_conn_t *conn,
1346 : const unsigned char *sm_state,
1347 : size_t sm_state_len)
1348 : {
1349 : /* We can only set the SM state when we're disconnected */
1350 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1351 0 : strophe_error(conn->ctx, "conn",
1352 : "SM state can only be set the when we're disconnected");
1353 0 : return XMPP_EINVOP;
1354 : }
1355 :
1356 0 : if (conn->sm_state) {
1357 0 : strophe_error(conn->ctx, "conn", "SM state is already set!");
1358 0 : return XMPP_EINVOP;
1359 : }
1360 :
1361 0 : if (sm_state_len < 5 * 6) {
1362 0 : strophe_error(conn->ctx, "conn", "Provided sm_state data is too short");
1363 0 : return XMPP_EINVOP;
1364 : }
1365 0 : struct sm_restore sm = {.conn = conn,
1366 : .state = sm_state,
1367 0 : .state_end = sm_state + sm_state_len,
1368 : .orig = sm_state};
1369 : /* Check for pointer wrap-around, which should never happen */
1370 0 : if (sm.state_end < sm.state) {
1371 0 : strophe_error(conn->ctx, "conn",
1372 : "Internal error, pointer wrapped around");
1373 0 : return XMPP_EINVOP;
1374 : }
1375 :
1376 0 : if (memcmp(sm.state, "\x1a\x00\x00\x00\x00", 5) != 0) {
1377 0 : strophe_error(conn->ctx, "conn", "Unknown sm_state version");
1378 0 : return XMPP_EINVOP;
1379 : }
1380 0 : sm.state += 5;
1381 :
1382 0 : conn->sm_state = strophe_alloc(conn->ctx, sizeof(*conn->sm_state));
1383 0 : if (!conn->sm_state)
1384 : return XMPP_EMEM;
1385 :
1386 0 : memset(conn->sm_state, 0, sizeof(*conn->sm_state));
1387 0 : conn->sm_state->ctx = conn->ctx;
1388 :
1389 0 : conn->sm_state->sm_support = 1;
1390 0 : conn->sm_state->sm_enabled = 1;
1391 0 : conn->sm_state->can_resume = 1;
1392 0 : conn->sm_state->resume = 1;
1393 :
1394 0 : int ret;
1395 0 : ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_sent_nr);
1396 0 : if (ret)
1397 0 : goto err_reload;
1398 :
1399 0 : ret = sm_load_u32(&sm, 0x1a, &conn->sm_state->sm_handled_nr);
1400 0 : if (ret)
1401 0 : goto err_reload;
1402 :
1403 0 : size_t id_len;
1404 0 : ret = sm_load_string(&sm, &conn->sm_state->id, &id_len);
1405 0 : if (ret)
1406 0 : goto err_reload;
1407 :
1408 0 : uint32_t len, i;
1409 0 : ret = sm_load_u32(&sm, 0x9a, &len);
1410 0 : if (ret)
1411 0 : goto err_reload;
1412 0 : conn->send_queue_user_len = conn->send_queue_len = len;
1413 0 : for (i = 0; i < len; i++) {
1414 0 : xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
1415 0 : if (!item) {
1416 0 : ret = XMPP_EMEM;
1417 0 : goto err_reload;
1418 : }
1419 0 : memset(item, 0, sizeof(*item));
1420 :
1421 0 : if (!conn->send_queue_tail) {
1422 0 : conn->send_queue_head = item;
1423 0 : conn->send_queue_tail = item;
1424 : } else {
1425 0 : conn->send_queue_tail->next = item;
1426 0 : conn->send_queue_tail = item;
1427 : }
1428 :
1429 0 : ret = sm_load_string(&sm, &item->data, &item->len);
1430 0 : if (ret)
1431 0 : goto err_reload;
1432 :
1433 0 : item->owner = XMPP_QUEUE_USER;
1434 : }
1435 :
1436 0 : ret = sm_load_u32(&sm, 0xba, &len);
1437 0 : if (ret)
1438 0 : goto err_reload;
1439 0 : for (i = 0; i < len; i++) {
1440 0 : xmpp_send_queue_t *item = strophe_alloc(conn->ctx, sizeof(*item));
1441 0 : if (!item) {
1442 0 : ret = XMPP_EMEM;
1443 0 : goto err_reload;
1444 : }
1445 0 : memset(item, 0, sizeof(*item));
1446 :
1447 0 : add_queue_back(&conn->sm_state->sm_queue, item);
1448 :
1449 0 : ret = sm_load_u32(&sm, 0x1a, &item->sm_h);
1450 0 : if (ret)
1451 0 : goto err_reload;
1452 0 : ret = sm_load_string(&sm, &item->data, &item->len);
1453 0 : if (ret)
1454 0 : goto err_reload;
1455 :
1456 0 : item->owner = XMPP_QUEUE_USER;
1457 : }
1458 :
1459 : return XMPP_EOK;
1460 :
1461 0 : err_reload:
1462 0 : xmpp_free_sm_state(conn->sm_state);
1463 : return ret;
1464 : }
1465 :
1466 0 : static int sm_store_u32(unsigned char **next_,
1467 : const unsigned char *const end,
1468 : uint8_t type,
1469 : uint32_t val)
1470 : {
1471 0 : unsigned char *next = *next_;
1472 0 : if (next + 5 > end)
1473 0 : return 1;
1474 0 : *next++ = type;
1475 0 : uint32_t v = htonl(val);
1476 0 : memcpy(next, &v, 4);
1477 0 : next += 4;
1478 0 : *next_ = next;
1479 0 : return 0;
1480 : }
1481 :
1482 0 : static size_t sm_state_serialize(xmpp_conn_t *conn, unsigned char **buf)
1483 : {
1484 0 : if (!conn->sm_state->sm_support || !conn->sm_state->sm_enabled ||
1485 0 : !conn->sm_state->can_resume) {
1486 0 : *buf = NULL;
1487 0 : return 0;
1488 : }
1489 :
1490 0 : uint32_t id_len = strlen(conn->sm_state->id);
1491 0 : xmpp_send_queue_t *peek = conn->sm_state->sm_queue.head;
1492 0 : size_t sm_queue_len = 0;
1493 0 : size_t sm_queue_size = 0;
1494 0 : while (peek) {
1495 0 : sm_queue_len++;
1496 0 : sm_queue_size += 10 + peek->len;
1497 0 : peek = peek->next;
1498 : }
1499 :
1500 0 : uint32_t send_queue_len = 0;
1501 0 : size_t send_queue_size = 0;
1502 0 : peek = conn->send_queue_head;
1503 0 : while (peek) {
1504 0 : send_queue_len++;
1505 0 : send_queue_size += 5 + peek->len;
1506 0 : peek = peek->next;
1507 : }
1508 :
1509 0 : size_t buf_size =
1510 0 : 5 + 5 + 5 + 5 + id_len + 5 + send_queue_size + 5 + sm_queue_size;
1511 0 : *buf = strophe_alloc(conn->ctx, buf_size);
1512 0 : if (*buf == NULL)
1513 : return 0;
1514 0 : unsigned char *next = *buf;
1515 0 : const unsigned char *const end = next + buf_size;
1516 : /* Check for pointer wrap-around, which should never happen */
1517 0 : if (end < next) {
1518 0 : strophe_error(conn->ctx, "conn",
1519 : "Internal error, pointer wrapped around");
1520 0 : return 0;
1521 : }
1522 :
1523 0 : memcpy(next, "\x1a\x00\x00\x00\x00", 5); // Version
1524 0 : next += 5;
1525 :
1526 0 : if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_sent_nr))
1527 0 : goto err_serialize;
1528 0 : if (sm_store_u32(&next, end, 0x1a, conn->sm_state->sm_handled_nr))
1529 0 : goto err_serialize;
1530 :
1531 0 : if (sm_store_u32(&next, end, 0x7a, id_len))
1532 0 : goto err_serialize;
1533 0 : memcpy(next, conn->sm_state->id, id_len);
1534 0 : next += id_len;
1535 :
1536 0 : if (sm_store_u32(&next, end, 0x9a, send_queue_len))
1537 0 : goto err_serialize;
1538 :
1539 0 : peek = conn->send_queue_head;
1540 0 : while (peek) {
1541 0 : if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
1542 0 : goto err_serialize;
1543 0 : if (next + peek->len > end)
1544 0 : goto err_serialize;
1545 0 : memcpy(next, peek->data, peek->len);
1546 0 : next += peek->len;
1547 0 : peek = peek->next;
1548 : }
1549 :
1550 0 : if (sm_store_u32(&next, end, 0xba, sm_queue_len))
1551 0 : goto err_serialize;
1552 :
1553 0 : peek = conn->sm_state->sm_queue.head;
1554 0 : while (peek) {
1555 0 : if (sm_store_u32(&next, end, 0x1a, peek->sm_h))
1556 0 : goto err_serialize;
1557 :
1558 0 : if (sm_store_u32(&next, end, 0x7a, (uint32_t)peek->len))
1559 0 : goto err_serialize;
1560 0 : if (next + peek->len > end)
1561 0 : goto err_serialize;
1562 0 : memcpy(next, peek->data, peek->len);
1563 0 : next += peek->len;
1564 0 : peek = peek->next;
1565 : }
1566 :
1567 : return buf_size;
1568 :
1569 0 : err_serialize:
1570 0 : strophe_error(conn->ctx, "conn", "Can't serialize more data, buffer full");
1571 0 : strophe_free(conn->ctx, buf);
1572 : return 0;
1573 : }
1574 :
1575 0 : void trigger_sm_callback(xmpp_conn_t *conn)
1576 : {
1577 0 : if (!conn || !conn->sm_callback)
1578 0 : return;
1579 :
1580 0 : unsigned char *buf;
1581 0 : size_t size = sm_state_serialize(conn, &buf);
1582 0 : conn->sm_callback(conn, conn->sm_callback_ctx, buf, size);
1583 0 : strophe_free(conn->ctx, buf);
1584 : }
1585 :
1586 0 : static void _reset_sm_state_for_reconnect(xmpp_conn_t *conn)
1587 : {
1588 0 : xmpp_sm_state_t *s = conn->sm_state;
1589 :
1590 0 : if (s->previd) {
1591 0 : strophe_free(conn->ctx, s->previd);
1592 0 : s->previd = NULL;
1593 : }
1594 :
1595 0 : if (s->can_resume) {
1596 0 : s->previd = s->id;
1597 0 : s->id = NULL;
1598 :
1599 0 : s->bound_jid = conn->bound_jid;
1600 0 : conn->bound_jid = NULL;
1601 0 : } else if (s->id) {
1602 0 : strophe_free(conn->ctx, s->id);
1603 0 : s->id = NULL;
1604 : }
1605 :
1606 0 : s->r_sent = s->sm_enabled = s->sm_support = s->resume = 0;
1607 :
1608 0 : if (s->bind) {
1609 0 : xmpp_stanza_release(s->bind);
1610 0 : s->bind = NULL;
1611 : }
1612 0 : }
1613 :
1614 : /**
1615 : * @param conn a Strophe connection object
1616 : * @param sm_state A Stream Management state returned from a call to
1617 : * `xmpp_conn_get_sm_state()`
1618 : *
1619 : * @return XMPP_EOK (0) on success or a number less than 0 on failure
1620 : *
1621 : * @ingroup Connections
1622 : */
1623 0 : int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state)
1624 : {
1625 : /* We can only set the SM state when we're disconnected */
1626 0 : if (conn->state != XMPP_STATE_DISCONNECTED) {
1627 0 : strophe_error(conn->ctx, "conn",
1628 : "SM state can only be set the when we're disconnected");
1629 0 : return XMPP_EINVOP;
1630 : }
1631 :
1632 0 : if (conn->sm_state) {
1633 0 : strophe_error(conn->ctx, "conn", "SM state is already set!");
1634 0 : return XMPP_EINVOP;
1635 : }
1636 :
1637 0 : if (conn->ctx != sm_state->ctx) {
1638 0 : strophe_error(
1639 : conn->ctx, "conn",
1640 : "SM state has to be assigned to connection that stems from "
1641 : "the same context!");
1642 0 : return XMPP_EINVOP;
1643 : }
1644 :
1645 0 : conn->sm_state = sm_state;
1646 0 : return XMPP_EOK;
1647 : }
1648 :
1649 0 : void reset_sm_state(xmpp_sm_state_t *sm_state)
1650 : {
1651 0 : xmpp_ctx_t *ctx = sm_state->ctx;
1652 :
1653 0 : strophe_free_and_null(ctx, sm_state->id);
1654 0 : strophe_free_and_null(ctx, sm_state->previd);
1655 0 : strophe_free_and_null(ctx, sm_state->bound_jid);
1656 0 : if (sm_state->bind)
1657 0 : xmpp_stanza_release(sm_state->bind);
1658 0 : sm_state->bind = NULL;
1659 0 : sm_state->sm_handled_nr = 0;
1660 0 : sm_state->sm_sent_nr = 0;
1661 0 : sm_state->r_sent = 0;
1662 0 : }
1663 :
1664 : /** c.f. \ref xmpp_conn_get_sm_state for usage documentation
1665 : *
1666 : * @param sm_state A Stream Management state returned from a call to
1667 : * `xmpp_conn_get_sm_state()`
1668 : *
1669 : * @ingroup Connections
1670 : */
1671 0 : void xmpp_free_sm_state(xmpp_sm_state_t *sm_state)
1672 : {
1673 0 : xmpp_send_queue_t *smq;
1674 0 : xmpp_ctx_t *ctx;
1675 :
1676 0 : if (!sm_state || !sm_state->ctx)
1677 : return;
1678 :
1679 : ctx = sm_state->ctx;
1680 :
1681 0 : while ((smq = pop_queue_front(&sm_state->sm_queue))) {
1682 0 : strophe_free(ctx, queue_element_free(ctx, smq));
1683 : }
1684 :
1685 0 : reset_sm_state(sm_state);
1686 0 : strophe_free(ctx, sm_state);
1687 : }
1688 :
1689 : /**
1690 : * @return The number of entries in the send queue
1691 : *
1692 : * @ingroup Connections
1693 : */
1694 0 : int xmpp_conn_send_queue_len(const xmpp_conn_t *conn)
1695 : {
1696 0 : if (conn->send_queue_head && conn->send_queue_head->wip &&
1697 0 : conn->send_queue_head->owner == XMPP_QUEUE_USER)
1698 0 : return conn->send_queue_user_len - 1;
1699 : else
1700 0 : return conn->send_queue_user_len;
1701 : }
1702 :
1703 0 : static char *_drop_send_queue_element(xmpp_conn_t *conn, xmpp_send_queue_t *e)
1704 : {
1705 0 : if (e == conn->send_queue_head)
1706 0 : conn->send_queue_head = e->next;
1707 0 : if (e == conn->send_queue_tail)
1708 0 : conn->send_queue_tail = e->prev;
1709 0 : if (!conn->send_queue_head)
1710 0 : conn->send_queue_tail = NULL;
1711 0 : if (e->prev)
1712 0 : e->prev->next = e->next;
1713 0 : if (e->next)
1714 0 : e->next->prev = e->prev;
1715 0 : conn->send_queue_len--;
1716 0 : if (e->owner == XMPP_QUEUE_USER)
1717 0 : conn->send_queue_user_len--;
1718 0 : return queue_element_free(conn->ctx, e);
1719 : }
1720 :
1721 : /** Drop an element of the send queue.
1722 : * This can be used to manage the send queue in case a server
1723 : * isn't fast enough in processing the elements you're trying
1724 : * to send or your outgoing bandwidth isn't fast enough to transfer
1725 : * everything you want to send out.
1726 : *
1727 : * @param conn a Strophe connection object
1728 : * @param which the element that shall be removed
1729 : *
1730 : * @return The rendered stanza. The pointer returned has to be free'd by the
1731 : * caller of this function.
1732 : *
1733 : * @ingroup Connections
1734 : */
1735 0 : char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
1736 : xmpp_queue_element_t which)
1737 : {
1738 0 : xmpp_send_queue_t *t;
1739 0 : int disconnected = conn->state == XMPP_STATE_DISCONNECTED;
1740 :
1741 : /* Fast return paths */
1742 : /* empty queue */
1743 0 : if (!conn->send_queue_head)
1744 : return NULL;
1745 : /* one element in queue */
1746 0 : if (conn->send_queue_head == conn->send_queue_tail) {
1747 : /* head is already sent out partially */
1748 0 : if (conn->send_queue_head->wip && !disconnected)
1749 : return NULL;
1750 : /* the element is no USER element */
1751 0 : if (conn->send_queue_head->owner != XMPP_QUEUE_USER)
1752 : return NULL;
1753 : }
1754 :
1755 : /* Regular flow */
1756 0 : if (which == XMPP_QUEUE_OLDEST) {
1757 : t = conn->send_queue_head;
1758 0 : } else if (which == XMPP_QUEUE_YOUNGEST) {
1759 : t = conn->send_queue_tail;
1760 : /* search backwards to find last USER element */
1761 0 : while (t && t->owner != XMPP_QUEUE_USER)
1762 0 : t = t->prev;
1763 : } else {
1764 0 : strophe_error(conn->ctx, "conn", "Unknown queue element %d", which);
1765 0 : return NULL;
1766 : }
1767 : /* there was no USER element in the queue */
1768 0 : if (!t)
1769 : return NULL;
1770 :
1771 : /* head is already sent out partially */
1772 0 : if (t == conn->send_queue_head && t->wip && !disconnected)
1773 0 : t = t->next;
1774 :
1775 : /* search forward to find the first USER element */
1776 0 : while (t && t->owner != XMPP_QUEUE_USER)
1777 0 : t = t->next;
1778 :
1779 : /* there was no USER element in the queue we could drop */
1780 0 : if (!t)
1781 : return NULL;
1782 :
1783 : /* In case there exists a SM stanza that is linked to the
1784 : * one we're currently dropping, also delete that one.
1785 : */
1786 0 : if (t->next && t->next->userdata == t) {
1787 0 : strophe_free(conn->ctx, _drop_send_queue_element(conn, t->next));
1788 : /* reset the flag, so we restart to send `<r>` stanzas */
1789 0 : conn->sm_state->r_sent = 0;
1790 : }
1791 : /* Finally drop the element */
1792 0 : char *r = _drop_send_queue_element(conn, t);
1793 0 : trigger_sm_callback(conn);
1794 0 : return r;
1795 : }
1796 :
1797 : /* timed handler for cleanup if normal disconnect procedure takes too long */
1798 0 : static int _disconnect_cleanup(xmpp_conn_t *conn, void *userdata)
1799 : {
1800 0 : UNUSED(userdata);
1801 :
1802 0 : strophe_debug(conn->ctx, "xmpp", "disconnection forced by cleanup timeout");
1803 :
1804 0 : conn_disconnect(conn);
1805 :
1806 0 : return 0;
1807 : }
1808 :
1809 0 : static char *_conn_build_stream_tag(xmpp_conn_t *conn,
1810 : char **attributes,
1811 : size_t attributes_len)
1812 : {
1813 0 : char *tag;
1814 0 : size_t len;
1815 0 : size_t i;
1816 :
1817 0 : static const char *tag_head = "<stream:stream";
1818 0 : static const char *tag_tail = ">";
1819 :
1820 : /* ignore the last element unless number is even */
1821 0 : attributes_len &= ~(size_t)1;
1822 :
1823 0 : len = strlen(tag_head) + strlen(tag_tail);
1824 0 : for (i = 0; i < attributes_len; ++i)
1825 0 : len += strlen(attributes[i]) + 2;
1826 0 : tag = strophe_alloc(conn->ctx, len + 1);
1827 0 : if (!tag)
1828 : return NULL;
1829 :
1830 0 : strcpy(tag, tag_head);
1831 0 : for (i = 0; i < attributes_len; ++i) {
1832 0 : if ((i & 1) == 0) {
1833 0 : strcat(tag, " ");
1834 0 : strcat(tag, attributes[i]);
1835 0 : strcat(tag, "=\"");
1836 : } else {
1837 0 : strcat(tag, attributes[i]);
1838 0 : strcat(tag, "\"");
1839 : }
1840 : }
1841 0 : strcat(tag, tag_tail);
1842 :
1843 0 : if (strlen(tag) != len) {
1844 0 : strophe_error(conn->ctx, "xmpp",
1845 : "Internal error in "
1846 : "_conn_build_stream_tag().");
1847 0 : strophe_free(conn->ctx, tag);
1848 0 : tag = NULL;
1849 : }
1850 :
1851 : return tag;
1852 : }
1853 :
1854 0 : static int _conn_open_stream_with_attributes(xmpp_conn_t *conn,
1855 : char **attributes,
1856 : size_t attributes_len)
1857 : {
1858 0 : char *tag;
1859 :
1860 0 : tag = _conn_build_stream_tag(conn, attributes, attributes_len);
1861 0 : if (!tag)
1862 : return XMPP_EMEM;
1863 :
1864 0 : send_raw_string(conn, "<?xml version=\"1.0\"?>%s", tag);
1865 0 : strophe_free(conn->ctx, tag);
1866 :
1867 0 : return XMPP_EOK;
1868 : }
1869 :
1870 0 : static void _conn_attributes_new(xmpp_conn_t *conn,
1871 : char **attrs,
1872 : char ***attributes,
1873 : size_t *attributes_len)
1874 : {
1875 0 : char **array = NULL;
1876 0 : size_t nr = 0;
1877 0 : size_t i;
1878 :
1879 0 : if (attrs) {
1880 0 : for (; attrs[nr]; ++nr)
1881 : ;
1882 0 : array = strophe_alloc(conn->ctx, sizeof(*array) * nr);
1883 0 : for (i = 0; array && i < nr; ++i) {
1884 0 : array[i] = (i & 1) == 0 ? parser_attr_name(conn->ctx, attrs[i])
1885 0 : : strophe_strdup(conn->ctx, attrs[i]);
1886 0 : if (array[i] == NULL)
1887 : break;
1888 : }
1889 0 : if (!array || i < nr) {
1890 0 : strophe_error(conn->ctx, "xmpp", "Memory allocation error.");
1891 0 : _conn_attributes_destroy(conn, array, i);
1892 0 : array = NULL;
1893 0 : nr = 0;
1894 : }
1895 : }
1896 0 : *attributes = array;
1897 0 : *attributes_len = nr;
1898 0 : }
1899 :
1900 0 : static void _conn_attributes_destroy(xmpp_conn_t *conn,
1901 : char **attributes,
1902 : size_t attributes_len)
1903 : {
1904 0 : size_t i;
1905 :
1906 0 : if (attributes) {
1907 0 : for (i = 0; i < attributes_len; ++i)
1908 0 : strophe_free(conn->ctx, attributes[i]);
1909 0 : strophe_free(conn->ctx, attributes);
1910 : }
1911 0 : }
1912 :
1913 0 : static void _log_open_tag(xmpp_conn_t *conn, char **attrs)
1914 : {
1915 0 : char **attributes;
1916 0 : char *tag;
1917 0 : size_t nr;
1918 :
1919 0 : _conn_attributes_new(conn, attrs, &attributes, &nr);
1920 0 : tag = _conn_build_stream_tag(conn, attributes, nr);
1921 0 : if (tag) {
1922 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", tag);
1923 0 : strophe_free(conn->ctx, tag);
1924 : }
1925 0 : _conn_attributes_destroy(conn, attributes, nr);
1926 0 : }
1927 :
1928 0 : static char *_get_stream_attribute(char **attrs, char *name)
1929 : {
1930 0 : int i;
1931 :
1932 0 : if (!attrs)
1933 : return NULL;
1934 :
1935 0 : for (i = 0; attrs[i]; i += 2)
1936 0 : if (strcmp(name, attrs[i]) == 0)
1937 0 : return attrs[i + 1];
1938 :
1939 : return NULL;
1940 : }
1941 :
1942 0 : static void _handle_stream_start(char *name, char **attrs, void *userdata)
1943 : {
1944 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1945 0 : char *id;
1946 0 : int failed = 0;
1947 :
1948 0 : if (conn->stream_id)
1949 0 : strophe_free(conn->ctx, conn->stream_id);
1950 0 : conn->stream_id = NULL;
1951 :
1952 0 : if (strcmp(name, "stream") == 0) {
1953 0 : _log_open_tag(conn, attrs);
1954 0 : id = _get_stream_attribute(attrs, "id");
1955 0 : if (id)
1956 0 : conn->stream_id = strophe_strdup(conn->ctx, id);
1957 :
1958 0 : if (id && !conn->stream_id) {
1959 0 : strophe_error(conn->ctx, "conn", "Memory allocation failed.");
1960 0 : failed = 1;
1961 : }
1962 : } else {
1963 0 : strophe_error(conn->ctx, "conn",
1964 : "Server did not open valid stream."
1965 : " name = %s.",
1966 : name);
1967 0 : failed = 1;
1968 : }
1969 :
1970 0 : if (!failed) {
1971 : /* call stream open handler */
1972 0 : conn->open_handler(conn);
1973 : } else {
1974 0 : conn_disconnect(conn);
1975 : }
1976 0 : }
1977 :
1978 0 : static void _handle_stream_end(char *name, void *userdata)
1979 : {
1980 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1981 :
1982 0 : UNUSED(name);
1983 :
1984 : /* stream is over */
1985 0 : strophe_debug(conn->ctx, "xmpp", "RECV: </stream:stream>");
1986 : /* the session has been terminated properly, i.e. it can't be resumed */
1987 0 : conn->sm_state->can_resume = 0;
1988 0 : trigger_sm_callback(conn);
1989 0 : conn_disconnect_clean(conn);
1990 0 : }
1991 :
1992 0 : static void _handle_stream_stanza(xmpp_stanza_t *stanza, void *userdata)
1993 : {
1994 0 : xmpp_conn_t *conn = (xmpp_conn_t *)userdata;
1995 0 : char *buf;
1996 0 : size_t len;
1997 :
1998 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) == 0) {
1999 0 : strophe_debug(conn->ctx, "xmpp", "RECV: %s", buf);
2000 0 : strophe_free(conn->ctx, buf);
2001 : }
2002 :
2003 0 : handler_fire_stanza(conn, stanza);
2004 0 : if (conn->sm_state->sm_enabled)
2005 0 : _conn_sm_handle_stanza(conn, stanza);
2006 0 : }
2007 :
2008 : /* XEP-0198 stream management */
2009 0 : static void _conn_sm_handle_stanza(xmpp_conn_t *const conn,
2010 : xmpp_stanza_t *stanza)
2011 : {
2012 0 : xmpp_stanza_t *a;
2013 0 : xmpp_send_queue_t *e;
2014 0 : char *c;
2015 0 : const char *name, *ns, *attr_h;
2016 0 : char h[11];
2017 0 : unsigned long ul_h;
2018 :
2019 0 : ns = xmpp_stanza_get_ns(stanza);
2020 0 : if (ns && strcmp(ns, XMPP_NS_SM) != 0)
2021 0 : ++conn->sm_state->sm_handled_nr;
2022 : else {
2023 0 : name = xmpp_stanza_get_name(stanza);
2024 0 : if (!name)
2025 0 : return;
2026 0 : if (strcmp(name, "r") == 0) {
2027 0 : a = xmpp_stanza_new(conn->ctx);
2028 0 : if (!a) {
2029 0 : strophe_debug(conn->ctx, "conn", "Couldn't create <a> stanza.");
2030 0 : return;
2031 : }
2032 0 : xmpp_stanza_set_name(a, "a");
2033 0 : xmpp_stanza_set_ns(a, XMPP_NS_SM);
2034 0 : strophe_snprintf(h, sizeof(h), "%u", conn->sm_state->sm_handled_nr);
2035 0 : xmpp_stanza_set_attribute(a, "h", h);
2036 0 : send_stanza(conn, a, XMPP_QUEUE_SM_STROPHE);
2037 0 : } else if (strcmp(name, "a") == 0) {
2038 0 : attr_h = xmpp_stanza_get_attribute(stanza, "h");
2039 0 : if (!attr_h) {
2040 0 : strophe_debug(conn->ctx, "conn", "Didn't find 'h' attribute.");
2041 0 : return;
2042 : }
2043 0 : if (string_to_ul(attr_h, &ul_h)) {
2044 0 : strophe_error(
2045 0 : conn->ctx, "conn",
2046 : "Error on strtoul() of '%s', returned value is %llu.",
2047 : attr_h, ul_h);
2048 : /* We continue here and drop the complete SM queue instead of
2049 : * returning and letting the queue fill up.
2050 : */
2051 0 : ul_h = ULONG_MAX;
2052 : }
2053 0 : while (conn->sm_state->sm_queue.head &&
2054 0 : conn->sm_state->sm_queue.head->sm_h < ul_h) {
2055 0 : e = pop_queue_front(&conn->sm_state->sm_queue);
2056 0 : strophe_debug_verbose(2, conn->ctx, "conn",
2057 : "SM_Q_DROP: %p, h=%lu", e, e->sm_h);
2058 0 : c = queue_element_free(conn->ctx, e);
2059 0 : strophe_free(conn->ctx, c);
2060 : }
2061 0 : conn->sm_state->r_sent = 0;
2062 : }
2063 : }
2064 0 : trigger_sm_callback(conn);
2065 : }
2066 :
2067 0 : static unsigned short _conn_default_port(xmpp_conn_t *conn,
2068 : xmpp_conn_type_t type)
2069 : {
2070 0 : switch (type) {
2071 0 : case XMPP_CLIENT:
2072 0 : return conn->tls_legacy_ssl ? XMPP_PORT_CLIENT_LEGACY_SSL
2073 0 : : XMPP_PORT_CLIENT;
2074 : case XMPP_COMPONENT:
2075 : return XMPP_PORT_COMPONENT;
2076 : default:
2077 : return 0;
2078 : };
2079 : }
2080 :
2081 0 : char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e)
2082 : {
2083 0 : char *ret = e->data;
2084 0 : strophe_debug_verbose(2, ctx, "conn", "Q_FREE: %p", e);
2085 0 : memset(e, 0, sizeof(*e));
2086 0 : strophe_free(ctx, e);
2087 0 : strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s", ret);
2088 0 : return ret;
2089 : }
2090 :
2091 8 : static void _conn_reset(xmpp_conn_t *conn)
2092 : {
2093 8 : xmpp_ctx_t *ctx = conn->ctx;
2094 8 : xmpp_send_queue_t *sq, *tsq;
2095 :
2096 8 : if (conn->state != XMPP_STATE_DISCONNECTED) {
2097 0 : strophe_debug(ctx, "conn", "Can't reset connected object.");
2098 0 : return;
2099 : }
2100 :
2101 8 : compression_free(conn);
2102 :
2103 8 : conn->intf = sock_intf;
2104 8 : conn->intf.conn = conn;
2105 :
2106 : /* free queued */
2107 8 : sq = conn->send_queue_head;
2108 8 : while (sq) {
2109 0 : tsq = sq;
2110 0 : sq = sq->next;
2111 0 : strophe_free(ctx, queue_element_free(ctx, tsq));
2112 : }
2113 8 : conn->send_queue_head = NULL;
2114 8 : conn->send_queue_tail = NULL;
2115 8 : conn->send_queue_len = 0;
2116 8 : conn->send_queue_user_len = 0;
2117 :
2118 8 : if (conn->stream_error) {
2119 0 : xmpp_stanza_release(conn->stream_error->stanza);
2120 0 : strophe_free_and_null(ctx, conn->stream_error->text);
2121 0 : strophe_free_and_null(ctx, conn->stream_error);
2122 : }
2123 :
2124 8 : strophe_free_and_null(ctx, conn->domain);
2125 8 : strophe_free_and_null(ctx, conn->bound_jid);
2126 8 : strophe_free_and_null(ctx, conn->stream_id);
2127 8 : conn->stream_negotiation_completed = 0;
2128 8 : conn->secured = 0;
2129 8 : conn->tls_failed = 0;
2130 8 : conn->error = 0;
2131 :
2132 8 : conn->tls_support = 0;
2133 :
2134 8 : conn->bind_required = 0;
2135 8 : conn->session_required = 0;
2136 :
2137 8 : handler_system_delete_all(conn);
2138 : }
2139 :
2140 0 : static int _conn_connect(xmpp_conn_t *conn,
2141 : const char *domain,
2142 : xmpp_conn_type_t type,
2143 : xmpp_conn_handler callback,
2144 : void *userdata)
2145 : {
2146 0 : xmpp_open_handler open_handler;
2147 :
2148 0 : if (conn->state != XMPP_STATE_DISCONNECTED)
2149 : return XMPP_EINVOP;
2150 0 : if (type != XMPP_CLIENT && type != XMPP_COMPONENT)
2151 : return XMPP_EINVOP;
2152 :
2153 0 : _conn_reset(conn);
2154 :
2155 0 : conn->type = type;
2156 0 : conn->domain = strophe_strdup(conn->ctx, domain);
2157 0 : if (!conn->domain)
2158 : return XMPP_EMEM;
2159 :
2160 0 : conn->sock = sock_connect(conn->xsock);
2161 0 : if (conn->sock == INVALID_SOCKET)
2162 : return XMPP_EINT;
2163 :
2164 : /* setup handler */
2165 0 : conn->conn_handler = callback;
2166 0 : conn->userdata = userdata;
2167 :
2168 0 : open_handler = conn->is_raw ? auth_handle_open_stub
2169 0 : : type == XMPP_CLIENT ? auth_handle_open
2170 0 : : auth_handle_component_open;
2171 0 : conn_prepare_reset(conn, open_handler);
2172 :
2173 : /* FIXME: it could happen that the connect returns immediately as
2174 : * successful, though this is pretty unlikely. This would be a little
2175 : * hard to fix, since we'd have to detect and fire off the callback
2176 : * from within the event loop */
2177 :
2178 0 : conn->state = XMPP_STATE_CONNECTING;
2179 0 : conn->timeout_stamp = time_stamp();
2180 :
2181 0 : return 0;
2182 : }
2183 :
2184 0 : void send_raw(xmpp_conn_t *conn,
2185 : const char *data,
2186 : size_t len,
2187 : xmpp_send_queue_owner_t owner,
2188 : void *userdata)
2189 : {
2190 0 : char *d;
2191 :
2192 0 : if (conn->state != XMPP_STATE_CONNECTED)
2193 : return;
2194 :
2195 0 : d = strophe_strndup(conn->ctx, data, len);
2196 0 : if (!d) {
2197 0 : strophe_error(conn->ctx, "conn", "Failed to strndup");
2198 0 : return;
2199 : }
2200 :
2201 0 : _send_raw(conn, d, len, owner, userdata);
2202 : }
2203 :
2204 0 : static void _send_valist(xmpp_conn_t *conn,
2205 : const char *fmt,
2206 : va_list ap,
2207 : xmpp_send_queue_owner_t owner)
2208 : {
2209 0 : va_list apdup;
2210 0 : size_t len;
2211 0 : char buf[1024]; /* small buffer for common case */
2212 0 : char *bigbuf;
2213 :
2214 0 : if (!_is_connected(conn, owner))
2215 0 : return;
2216 :
2217 0 : va_copy(apdup, ap);
2218 0 : len = strophe_vsnprintf(buf, sizeof(buf), fmt, apdup);
2219 0 : va_end(apdup);
2220 :
2221 0 : if (len >= sizeof(buf)) {
2222 : /* we need more space for this data, so we allocate a big
2223 : * enough buffer and print to that */
2224 0 : len++; /* account for trailing \0 */
2225 0 : bigbuf = strophe_alloc(conn->ctx, len);
2226 0 : if (!bigbuf) {
2227 0 : strophe_debug(conn->ctx, "xmpp",
2228 : "Could not allocate memory for send_raw_string");
2229 0 : return;
2230 : }
2231 0 : va_copy(apdup, ap);
2232 0 : strophe_vsnprintf(bigbuf, len, fmt, apdup);
2233 0 : va_end(apdup);
2234 :
2235 : /* len - 1 so we don't send trailing \0 */
2236 0 : _send_raw(conn, bigbuf, len - 1, owner, NULL);
2237 : } else {
2238 : /* go through send_raw() which does the strdup() for us */
2239 0 : send_raw(conn, buf, len, owner, NULL);
2240 : }
2241 : }
2242 :
2243 0 : void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...)
2244 : {
2245 0 : va_list ap;
2246 :
2247 0 : if (conn->state != XMPP_STATE_CONNECTED)
2248 0 : return;
2249 :
2250 0 : va_start(ap, fmt);
2251 0 : _send_valist(conn, fmt, ap, XMPP_QUEUE_SM_STROPHE);
2252 0 : va_end(ap);
2253 : }
2254 :
2255 0 : void send_stanza(xmpp_conn_t *conn,
2256 : xmpp_stanza_t *stanza,
2257 : xmpp_send_queue_owner_t owner)
2258 : {
2259 0 : char *buf = NULL;
2260 0 : size_t len;
2261 :
2262 0 : if (!_is_connected(conn, owner))
2263 0 : goto out;
2264 :
2265 0 : if (xmpp_stanza_to_text(stanza, &buf, &len) != 0) {
2266 0 : strophe_error(conn->ctx, "conn", "Failed to stanza_to_text");
2267 0 : goto out;
2268 : }
2269 :
2270 0 : _send_raw(conn, buf, len, owner, NULL);
2271 0 : out:
2272 0 : xmpp_stanza_release(stanza);
2273 0 : }
2274 :
2275 0 : void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item)
2276 : {
2277 0 : item->next = NULL;
2278 0 : if (!queue->tail) {
2279 0 : item->prev = NULL;
2280 0 : queue->head = item;
2281 0 : queue->tail = item;
2282 : } else {
2283 0 : item->prev = queue->tail;
2284 0 : queue->tail->next = item;
2285 0 : queue->tail = item;
2286 : }
2287 0 : }
2288 :
2289 0 : xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue)
2290 : {
2291 0 : return queue->head;
2292 : }
2293 :
2294 0 : xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue)
2295 : {
2296 0 : xmpp_send_queue_t *ret = queue->head;
2297 0 : if (queue->head) {
2298 0 : queue->head = queue->head->next;
2299 0 : if (!queue->head) {
2300 0 : queue->tail = NULL;
2301 : } else {
2302 0 : queue->head->prev = NULL;
2303 : }
2304 0 : ret->prev = ret->next = NULL;
2305 : }
2306 0 : return ret;
2307 : }
2308 :
2309 0 : static int _send_raw(xmpp_conn_t *conn,
2310 : char *data,
2311 : size_t len,
2312 : xmpp_send_queue_owner_t owner,
2313 : void *userdata)
2314 : {
2315 0 : xmpp_send_queue_t *item;
2316 0 : const char *req_ack = "<r xmlns='urn:xmpp:sm:3'/>";
2317 :
2318 : /* create send queue item for queue */
2319 0 : item = strophe_alloc(conn->ctx, sizeof(xmpp_send_queue_t));
2320 0 : if (!item) {
2321 0 : strophe_error(conn->ctx, "conn", "DROPPED: %s", data);
2322 0 : strophe_free(conn->ctx, data);
2323 0 : return XMPP_EMEM;
2324 : }
2325 :
2326 0 : item->data = data;
2327 0 : item->len = len;
2328 0 : item->next = NULL;
2329 0 : item->prev = conn->send_queue_tail;
2330 0 : item->written = 0;
2331 0 : item->wip = 0;
2332 0 : item->userdata = userdata;
2333 0 : item->owner = owner;
2334 :
2335 0 : if (!conn->send_queue_tail) {
2336 : /* first item, set head and tail */
2337 0 : conn->send_queue_head = item;
2338 0 : conn->send_queue_tail = item;
2339 : } else {
2340 : /* add to the tail */
2341 0 : conn->send_queue_tail->next = item;
2342 0 : conn->send_queue_tail = item;
2343 : }
2344 0 : conn->send_queue_len++;
2345 0 : if (owner == XMPP_QUEUE_USER)
2346 0 : conn->send_queue_user_len++;
2347 0 : strophe_debug_verbose(3, conn->ctx, "conn", "QUEUED: %s", data);
2348 0 : strophe_debug_verbose(1, conn->ctx, "conn", "Q_ADD: %p", item);
2349 0 : if (!(owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled &&
2350 0 : !conn->sm_state->r_sent) {
2351 0 : conn->sm_state->r_sent = 1;
2352 0 : send_raw(conn, req_ack, strlen(req_ack), XMPP_QUEUE_SM_STROPHE, item);
2353 : } else {
2354 0 : trigger_sm_callback(conn);
2355 : }
2356 : return XMPP_EOK;
2357 : }
|